/**
 * Copyright [2012-2013] [Guðmundur Björn Birkisson]
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. Copyright [2012-2013] [Guðmundur Björn Birkisson]
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * ---------------------------------------------------------------------------
 * 
 * Class name:  Program
 * 
 * Description of class:
 * 
 * Concrete implementation of LangObject
 * 
 */

package karel.lang.secondpass;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import karel.lang.RDParser;
import karel.lang.RDParserException;


public class Program extends LangObject {
    
    public class SearchResults {
        public int level;
        public int pos;
        
        public SearchResults(int level, int pos) {
            this.level = level;
            this.pos = pos;
        }
    }
    
    public class SearchQuery {
        public String name;
        public int arguments;
        
        public SearchQuery(String name, int arguments) {
            this.name = name;
            this.arguments = arguments;
        }
        
    }
    
    protected List<LangObject> objs;
    protected List<VarCall> varcalls;
    protected int currentPos;
    
    public Program(int line, int column) {
        super(line,column);
        objs = new LinkedList<LangObject>();
        varcalls =  new LinkedList<VarCall>();
        currentPos = 0;
    }
    
    public void add(LangObject obj) throws IOException {
        obj.parent = this;
        
        
        if(obj instanceof VarDecl) {
            ((VarDecl)obj).pos = currentPos++;
            SearchResults r = find(((VarDecl)obj).name,-1);
            if(r != null && r.level == 0) {
                String msg;
                if(obj instanceof ParamDecl) {
                    msg = "Viðfangið '"+((VarDecl)obj).name+"' nú þegar skilgreint í þessu falli!";
                } else {
                    msg = "Breytan '"+((VarDecl)obj).name+"' nú þegar skilgreind!";
                }
                throw new RDParserException(msg,
                        ((VarDecl)obj).line,((VarDecl)obj).column,((VarDecl)obj).name.length());
            }
        } else if(obj instanceof FunDecl) {
            ((FunDecl)obj).pos = currentPos++;
            objs.add(0, obj);
            return;
        } else if(obj instanceof VarCall) {
            varcalls.add(((VarCall)obj));
        } else if(obj instanceof Return) {
            throw new RDParserException("Aðeins má 'skila' úr stefum!",
                        ((Return)obj).line,((Return)obj).column,"skila".length());
        }
        
        objs.add(obj);
    }
    
    private void validateVarCalls() throws IOException {
        for(VarCall var: varcalls) {
            var.validateVarCall();
        }
    }

    @Override
    public void generate(RDParser parser) throws IOException {
        validateVarCalls();
        for(LangObject obj:objs) {
            obj.generate(parser);
        }
    }

    public SearchResults find(String sname, int arguments) {
        if(arguments < 0) {
            return findVar(sname, arguments, 0);
        } else {
            return findFunc(sname, arguments, 0);
        }
    }
    
    private SearchResults findVar(String sname, int arguments, int level) {
        SearchResults res = null;
        for(LangObject obj:objs) {
            if(obj instanceof VarDecl && ((VarDecl)obj).name.equals(sname)) {
                res = new SearchResults(level,((VarDecl)obj).pos);
                break;
            }
        }
        if(res == null && parent != null) {
            res = parent.findVar(sname, arguments, level+1);
        }
        return res;
    }
    
    private SearchResults findFunc(String sname, int arguments, int level) {
        SearchResults res = null;
        for(LangObject obj:objs) {
            if(obj instanceof FunDecl && ((FunDecl)obj).name.equals(sname) && ((FunDecl)obj).getNrOfParams() == arguments) {
                res = new SearchResults(level,((FunDecl)obj).pos);
                break;
            }
        }
        if(res == null && parent != null) {
            res = parent.findFunc(sname, arguments, level+1);
        }
        return res;
    }
    
    public void printTree() {
        printTreeRecursive("  ", "");
    }
    
    protected void printTreeRecursive(String gap, String currentGap) {
        System.out.println(currentGap + print());
        currentGap += gap;
        for(LangObject obj:objs) {
            if(obj instanceof FunDecl) {
                 ((FunDecl)obj).printTreeRecursive(gap, currentGap);
            } else {
                System.out.println(currentGap + obj.print());
            }
            
        }
    }
}
